/*
 * Copyright 2016 - 2018 NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file bcc_communication.c
 *
 * This file implements functions for SPI communication of BCC driver used in both
 * SPI and TPL mode.
 */

/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "../MC3377X_USER.h"
#ifdef MC33771C
#include "mc33771c_communication.h"
#include "bcc_mc33771c.h"
#include "mc33771c_peripheries.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*! @brief Size of CRC table. */
#define BCC_CRC_TBL_SIZE    256U

/*!
 * @brief This macro determines format of a MC33771 frame with use of register
 * address. Following registers only use TAG ID (reading): SYS_DIAG (5),
 * FAULT1_STATUS (24), FAULT2_STATUS (25), FAULT3_STATUS (26) and from
 * CC_NB_SAMPLES (2D) to MEAS_ VBG_DIAG_ADC1B (4A).
 *
 * @param regAddr Address of a register (typically extracted from received
 *                frame).
 *
 * @return True if format of frame with selected register address use TAG ID,
 *         false otherwise.
 */
/*
#define BCC_HAS_TAG_ID_MC33771(regAddr) \
  (((regAddr) == BCC_REG_SYS_DIAG_ADDR) || \
   ((regAddr) == BCC_REG_FAULT1_STATUS_ADDR) || \
   ((regAddr) == BCC_REG_FAULT2_STATUS_ADDR) || \
   ((regAddr) == BCC_REG_FAULT3_STATUS_ADDR) || \
   (((regAddr) >= BCC_REG_CC_NB_SAMPLES_ADDR) && \
   ((regAddr) <= BCC_REG_MEAS_VBG_DIAG_ADC1B_ADDR)) \
  )
*/

/*******************************************************************************
 * Constants and global variables
 ******************************************************************************/

/* Table with precalculated CRC values. */
static const uint8_t BCC_CRC_TABLE[BCC_CRC_TBL_SIZE] = {
    0x00U, 0x2fU, 0x5eU, 0x71U, 0xbcU, 0x93U, 0xe2U, 0xcdU, 0x57U, 0x78U, 0x09U, 0x26U, 0xebU, 0xc4U, 0xb5U, 0x9aU,
    0xaeU, 0x81U, 0xf0U, 0xdfU, 0x12U, 0x3dU, 0x4cU, 0x63U, 0xf9U, 0xd6U, 0xa7U, 0x88U, 0x45U, 0x6aU, 0x1bU, 0x34U,
    0x73U, 0x5cU, 0x2dU, 0x02U, 0xcfU, 0xe0U, 0x91U, 0xbeU, 0x24U, 0x0bU, 0x7aU, 0x55U, 0x98U, 0xb7U, 0xc6U, 0xe9U,
    0xddU, 0xf2U, 0x83U, 0xacU, 0x61U, 0x4eU, 0x3fU, 0x10U, 0x8aU, 0xa5U, 0xd4U, 0xfbU, 0x36U, 0x19U, 0x68U, 0x47U,
    0xe6U, 0xc9U, 0xb8U, 0x97U, 0x5aU, 0x75U, 0x04U, 0x2bU, 0xb1U, 0x9eU, 0xefU, 0xc0U, 0x0dU, 0x22U, 0x53U, 0x7cU,
    0x48U, 0x67U, 0x16U, 0x39U, 0xf4U, 0xdbU, 0xaaU, 0x85U, 0x1fU, 0x30U, 0x41U, 0x6eU, 0xa3U, 0x8cU, 0xfdU, 0xd2U,
    0x95U, 0xbaU, 0xcbU, 0xe4U, 0x29U, 0x06U, 0x77U, 0x58U, 0xc2U, 0xedU, 0x9cU, 0xb3U, 0x7eU, 0x51U, 0x20U, 0x0fU,
    0x3bU, 0x14U, 0x65U, 0x4aU, 0x87U, 0xa8U, 0xd9U, 0xf6U, 0x6cU, 0x43U, 0x32U, 0x1dU, 0xd0U, 0xffU, 0x8eU, 0xa1U,
    0xe3U, 0xccU, 0xbdU, 0x92U, 0x5fU, 0x70U, 0x01U, 0x2eU, 0xb4U, 0x9bU, 0xeaU, 0xc5U, 0x08U, 0x27U, 0x56U, 0x79U,
    0x4dU, 0x62U, 0x13U, 0x3cU, 0xf1U, 0xdeU, 0xafU, 0x80U, 0x1aU, 0x35U, 0x44U, 0x6bU, 0xa6U, 0x89U, 0xf8U, 0xd7U,
    0x90U, 0xbfU, 0xceU, 0xe1U, 0x2cU, 0x03U, 0x72U, 0x5dU, 0xc7U, 0xe8U, 0x99U, 0xb6U, 0x7bU, 0x54U, 0x25U, 0x0aU,
    0x3eU, 0x11U, 0x60U, 0x4fU, 0x82U, 0xadU, 0xdcU, 0xf3U, 0x69U, 0x46U, 0x37U, 0x18U, 0xd5U, 0xfaU, 0x8bU, 0xa4U,
    0x05U, 0x2aU, 0x5bU, 0x74U, 0xb9U, 0x96U, 0xe7U, 0xc8U, 0x52U, 0x7dU, 0x0cU, 0x23U, 0xeeU, 0xc1U, 0xb0U, 0x9fU,
    0xabU, 0x84U, 0xf5U, 0xdaU, 0x17U, 0x38U, 0x49U, 0x66U, 0xfcU, 0xd3U, 0xa2U, 0x8dU, 0x40U, 0x6fU, 0x1eU, 0x31U,
    0x76U, 0x59U, 0x28U, 0x07U, 0xcaU, 0xe5U, 0x94U, 0xbbU, 0x21U, 0x0eU, 0x7fU, 0x50U, 0x9dU, 0xb2U, 0xc3U, 0xecU,
    0xd8U, 0xf7U, 0x86U, 0xa9U, 0x64U, 0x4bU, 0x3aU, 0x15U, 0x8fU, 0xa0U, 0xd1U, 0xfeU, 0x33U, 0x1cU, 0x6dU, 0x42U
};

/*******************************************************************************
 * Prototypes of internal functions
 ******************************************************************************/

/*!
 * @brief This function calculates CRC value of passed data array.
 *
 * @param data An array used for CRC calculation.
 * @param dataLen Length of the array Data.
 *
 * @return Computed CRC value.
 */
static uint8_t BCC_CalcCRC(const uint8_t *data, uint8_t dataLen);

/*******************************************************************************
 * Internal function
 ******************************************************************************/

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_CalcCRC
 * Description   : This function calculates CRC value of passed data array.
 *
 *END**************************************************************************/
static uint8_t BCC_CalcCRC(const uint8_t *data, uint8_t dataLen)
{
    uint8_t crc;      /* Result. */
    uint8_t tableIdx; /* Index to the CRC table. */
    uint8_t dataIdx;  /* Index to the data array (memory). */

    /* Expanding value. */
    crc = 0x42U;

    for (dataIdx = 0U; dataIdx < dataLen; dataIdx++)
    {
#ifdef BCC_MSG_BIGEND
        tableIdx = crc ^ (*(data + dataIdx));
#else
        tableIdx = crc ^ (*(data + BCC_MSG_SIZE - 1 - dataIdx));
#endif
        crc = BCC_CRC_TABLE[tableIdx];
    }

    return crc;
}

/******************************************************************************
 * API
 ******************************************************************************/

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_PackFrame
 * Description   : This function packs all the parameters into a frame according
 *                 to the BCC frame format (see BCC datasheet).
 *
 *END**************************************************************************/
void BCC_PackFrame(uint16_t data, uint8_t addr, uint8_t cid, uint8_t cmd,
    uint8_t frame[])
{
    /* Memory Data field. */
    frame[BCC_MSG_IDX_DATA_H] = (uint8_t)(data >> 8U);
    frame[BCC_MSG_IDX_DATA_L] = (uint8_t)(data & 0xFFU);

    /* Memory Address fields. Master/Slave field is always 0 for sending. */
    frame[BCC_MSG_IDX_ADDR] = (addr & BCC_MSG_ADDR_MASK);

    /* Physical Address (Cluster ID). */
    frame[BCC_MSG_IDX_CID] = (cid & 0x3FU);

    /* Command field. */
    frame[BCC_MSG_IDX_CMD] = (cmd & 0xFFU);

    /* CRC field. */
    frame[BCC_MSG_IDX_CRC] = BCC_CalcCRC(frame, BCC_MSG_SIZE - 1U);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_CheckCRC
 * Description   : This function calculates CRC of a received frame and compares
 *                 it with CRC field of the frame.
 *
 *END**************************************************************************/
bcc_status_t BCC_CheckCRC(const uint8_t *resp)
{
    uint8_t frameCrc;  /* CRC value from resp. */
    uint8_t compCrc;   /* Computed CRC value. */

    /* Check CRC. */
    frameCrc = *(uint8_t *)(resp + BCC_MSG_IDX_CRC);
    compCrc = BCC_CalcCRC(resp, BCC_MSG_SIZE - 1U);
    return (compCrc != frameCrc) ? BCC_STATUS_CRC : BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_CheckRcTagId
 * Description   : This function checks value of the Command field of a frame.
 *
 *END**************************************************************************/
bcc_status_t BCC_CheckRcAddress(bcc_device_t devType, const uint8_t *resp, uint8_t rc, uint8_t address)
{
    uint8_t field;    /* Command field of a frame. */
    uint8_t regAddr;  /* Address field from a frame. */

    field = *(uint8_t *)(resp + BCC_MSG_IDX_CMD);
    regAddr = *(uint8_t *)(resp + BCC_MSG_IDX_ADDR) & BCC_MSG_ADDR_MASK;

	/* Check message counter value. */
	if ((field & BCC_MSG_RC_MASK) != (rc & BCC_MSG_RC_MASK))
	{
		return BCC_STATUS_COM_RC;
	}

	/* Check address value. */
	if (regAddr != address)
	{
	    return BCC_STATUS_COM_ADDR;
	}

    return BCC_STATUS_SUCCESS;
}

/*!
 * This following implements low level access functions for SPI communication of BCC
 * driver.
 */

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*!
 * @brief Returns true when a response message is equal to zero except
 * CRC field.
 *
 * @param resp Response message to be checked.
 * @return True when the response is zero (except CRC), false otherwise.
 */
#define BCC_IS_NULL_RESP(resp) \
    (((resp)[BCC_MSG_IDX_DATA_H] == 0U) && ((resp)[BCC_MSG_IDX_DATA_L] == 0U) && \
     ((resp)[BCC_MSG_IDX_ADDR] == 0U) && ((resp)[BCC_MSG_IDX_CID] == 0U)&& \
	 ((resp)[BCC_MSG_IDX_CMD] == 0U))

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_ReadRegistersSpi
 * Description   : This function reads a value from addressed register of selected
 *                 Battery Cell Controller device. Intended for SPI mode only.
 *
 *END**************************************************************************/
bcc_status_t BCC_ReadRegistersSpi(bcc_drv_config_t* const drvConfig,
        bcc_cid_t cid, uint8_t regAddr, uint8_t regCnt, uint16_t* regVal)
{

    return BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_WriteRegisterSpi
 * Description   : This function writes a value to addressed register of selected
 *                 Battery Cell Controller device. Intended for SPI mode only.
 *
 *END**************************************************************************/
bcc_status_t BCC_WriteRegisterSpi(bcc_drv_config_t* const drvConfig, bcc_cid_t cid, uint8_t regAddr, uint16_t regVal)
{

    return BCC_STATUS_SUCCESS;
}

/******************************************************************************/
/*!
 * @file bcc_tpl.c
 *
 * This file implements low level access functions for SPI communication of BCC
 * driver in TPL mode.
 */

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*! @brief This macro returns number of bytes required for receiving RegCnt
 *  frames.
 *
 * @param regCnt Number of register to be read.
 */
#define BCC_GET_RX_SIZE(regCnt) \
    (BCC_MSG_SIZE * ((uint16_t)(regCnt) + 1U))

/******************************************************************************
 * API
 ******************************************************************************/

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_ReadRegistersTpl
 * Description   : This function reads a value from addressed register of
 *                 selected Battery Cell Controller device. Intended for TPL
 *                 mode only.
 *
 *END**************************************************************************/
bcc_status_t BCC_ReadRegistersTpl(bcc_drv_config_t* const drvConfig,
        bcc_cid_t cid, uint8_t regAddr, uint8_t regCnt, uint16_t* regVal)
{
    uint8_t txBuf[BCC_MSG_SIZE]; /* Transmission buffer. */
    uint8_t *rxBuf = NULL;       /* Pointer to received data. */
    uint8_t regIdx;              /* Index of a received register. */
    uint8_t rc;                  /* Rolling Counter value. */
	uint8_t address;             /* register address value. */
    bcc_status_t error;

    if ((cid > drvConfig->devicesCnt) || (regAddr > BCC_MAX_REG_ADDR) ||
            (regCnt == 0U) || (regCnt > BCC_RX_LIMIT_TPL))
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    /* Create frame for request. */
    BCC_PackFrame((uint16_t)regCnt, regAddr, cid, BCC_CMD_READ , txBuf);

    error = BCC_TransferTpl(drvConfig->drvInstance, txBuf,
            drvConfig->drvData.rxBuf, BCC_MSG_SIZE, BCC_GET_RX_SIZE(regCnt));
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Check and store responses. */
    for (regIdx = 0U; regIdx < regCnt; regIdx++)
    {
        /* Pointer to beginning of a frame. */
        rxBuf = (uint8_t *)(drvConfig->drvData.rxBuf + ((1U + regIdx) * BCC_MSG_SIZE));
        if ((error = BCC_CheckCRC(rxBuf)) != BCC_STATUS_SUCCESS)
        {
            return error;
        }

        if (cid > BCC_CID_UNASSIG)
        {
            /* message counter and address checking */
        	address = regAddr + regIdx;

        	if(regIdx == 0)
        	{
        		rc = rxBuf[BCC_MSG_IDX_CMD];
        	}
            error = BCC_CheckRcAddress(drvConfig->device[cid - 1U], rxBuf, rc , address);

            if (error != BCC_STATUS_SUCCESS)
            {
                return error;
            }
            /*the value of the message counter increases by one unit for any frame it sends back*/
            rc = (rc+0x10)&0xF0;
        }

        /* Store data. */
        *(regVal + regIdx) = BCC_GET_MSG_DATA(rxBuf);
    }


    return BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_WriteRegisterTpl
 * Description   : This function writes a value to addressed register of
 *                 selected Battery Cell Controller device. Intended for TPL
 *                 mode only.
 *
 *END**************************************************************************/
bcc_status_t BCC_WriteRegisterTpl(bcc_drv_config_t* const drvConfig, bcc_cid_t cid, uint8_t regAddr, uint16_t regVal)
{
    uint8_t txBuf[BCC_MSG_SIZE]; /* Transmission buffer. */
    bcc_status_t error;

    if ((cid > drvConfig->devicesCnt) || (regAddr > BCC_MAX_REG_ADDR))
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    /* Create frame for writing. */
    BCC_PackFrame(regVal, regAddr, cid, BCC_CMD_WRITE, txBuf);

    if ((error = BCC_TransferTpl(drvConfig->drvInstance, txBuf,
            drvConfig->drvData.rxBuf, BCC_MSG_SIZE, 1 * BCC_MSG_SIZE)) != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    return BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_WriteRegisterGlobalTpl
 * Description   : This function writes a value to addressed register of all
 *                 configured BCC devices. Intended for TPL mode only.
 *
 *END**************************************************************************/
bcc_status_t BCC_WriteRegisterGlobalTpl(bcc_drv_config_t* const drvConfig,
        uint8_t regAddr, uint16_t regVal)
{
    uint8_t txBuf[BCC_MSG_SIZE]; /* Buffer for sending data via TPL. */
    bcc_status_t error;

    /* Check input parameters. */
    if (regAddr > BCC_MAX_REG_ADDR)
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    /* Create frame for writing. */
    BCC_PackFrame(regVal, regAddr, BCC_CID_UNASSIG, BCC_CMD_GLOB_WRITE, txBuf);

    error = BCC_TransferTpl(drvConfig->drvInstance, txBuf,
            drvConfig->drvData.rxBuf, BCC_MSG_SIZE, BCC_MSG_SIZE);

    return error;
}
#endif
